package base.juc;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 8个 synchronized 的使用场景
 * Author: shen qi han
 * Date: 2022/3/20 下午11:21
 */
public class SynchronizedMain {


    /**
     * add count number
     */
    static int count = 0;

    static int count2 = 0;

    /**
     * use to mark validate opt times
     * the countDownLatch is safe operation
     */
    CountDownLatch cdl = new CountDownLatch(100);

    CountDownLatch cdl2 = new CountDownLatch(100);

    CountDownLatch cdl3 = new CountDownLatch(2);

    class MyBlockTask implements Runnable{

        public void runTask(){
            // lock the code block
            synchronized(this){
                int num = 100;
                System.out.println(Thread.currentThread().getName() + " begin.");
                for(int i = 0; i < num; i++){
                    count++;
                }
                System.out.println(Thread.currentThread().getName() + " end.");
            }
        }

        @Override
        public void run() {
            runTask();
            cdl.countDown();
        }
    }

    class MyBlockTaskNotSafe implements Runnable{

        public void runTask(){
            // lock the code block
            int num = 100;
            System.out.println(Thread.currentThread().getName() + " begin.");
            for(int i = 0; i < num; i++){
                count2++;
            }
            System.out.println(Thread.currentThread().getName() + " end.");
        }

        @Override
        public void run() {
            runTask();
            cdl2.countDown();
        }
    }

    class MyBlockTaskAtMethod implements Runnable{

        public synchronized void runTask(){
            // lock the code block
            int num = 100;
            System.out.println(Thread.currentThread().getName() + " begin.");
            for(int i = 0; i < num; i++){
                count++;
            }
            System.out.println(Thread.currentThread().getName() + " end.");
        }

        @Override
        public void run() {
            runTask();
            cdl3.countDown();
        }
    }

    class MyBlockTaskRunUnSafeFunc implements Runnable{

        public synchronized void runTask(){
            // lock the code block
            int num = 100;
            System.out.println(Thread.currentThread().getName() + " 同步方法 begin.");
            for(int i = 0; i < num; i++){
                count++;
            }
            System.out.println(Thread.currentThread().getName() + " 同步方法 end.");
        }

        public void unSafeFunc(){
            System.out.println(Thread.currentThread().getName() + " 普通方法 begin");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " 普通方法 end");
        }

        @Override
        public void run() {
            // thread 1 run safe method
            // thread 2 run unsafe method
            String name = "pool-1-thread-1";
            if(name.equals(Thread.currentThread().getName())){
                runTask();
            }else{
                unSafeFunc();
            }

            cdl3.countDown();
        }
    }

    class MyBlockFun1AndFun2 implements Runnable{

        public synchronized void runTask1(){
            // lock the code block
            int num = 100;
            System.out.println(Thread.currentThread().getName() + " 同步方法 begin.");
            for(int i = 0; i < num; i++){
                count++;
            }
            System.out.println(Thread.currentThread().getName() + " 同步方法 end.");
        }

        public synchronized void runTask2(){
            // lock the code block
            int num = 100;
            System.out.println(Thread.currentThread().getName() + " 同步方法 begin.");
            for(int i = 0; i < num; i++){
                count++;
            }
            System.out.println(Thread.currentThread().getName() + " 同步方法 end.");
        }

        @Override
        public void run() {
            // thread 1 run safe method
            // thread 2 run unsafe method
            String name = "pool-1-thread-1";
            if(name.equals(Thread.currentThread().getName())){
                runTask1();
            }else{
                runTask2();
            }

            cdl3.countDown();
        }
    }

    class MyBlockFun1ThrowsAndFun2 implements Runnable{

        public synchronized void runTask1(){
            // lock the code block
            int num = 100;
            System.out.println(Thread.currentThread().getName() + " 同步方法 begin.");
            for(int i = 0; i < num; i++){
                count++;
            }
            cdl3.countDown();
            System.out.println(Thread.currentThread().getName() + " 抛出异常 end.");
            throw new RuntimeException();
        }

        public synchronized void runTask2(){
            // lock the code block
            int num = 100;
            System.out.println(Thread.currentThread().getName() + " 同步方法 begin.");
            for(int i = 0; i < num; i++){
                count++;
            }
            cdl3.countDown();
            System.out.println(Thread.currentThread().getName() + " 同步方法 end.");
        }

        @Override
        public void run() {
            // thread 1 run safe method
            // thread 2 run unsafe method
            String name = "pool-1-thread-1";
            if(name.equals(Thread.currentThread().getName())){
                runTask1();
            }else{
                runTask2();
            }
        }
    }

    /**
     * 通过同步代码快的方式
     *
     * result:
     * the count expect 10000, and the safe count result is 10000
     * the count expect 10000, and the unsafe count2 result is 9954
     *
     * view:
     * we can see the unsafe cnt++ operation will lead the val is not right
     * and use synchronized the thread will self-one to run cur code, the thread is in order
     * but not use synchronized the thread both can operation the cur code, so will get wrong result
     * if we use synchronized to block the code, it will safe operation
     *
     */
    public void lockTheCodeBlock(){
        // cur task
        // safe opt
        MyBlockTask task = new MyBlockTask();
        // not safe opt
        MyBlockTaskNotSafe unSafeTask = new MyBlockTaskNotSafe();

        int num = 100;
        ExecutorService pool = Executors.newFixedThreadPool(num);
        for(int i = 0; i < num; i++){
            pool.submit(task);
        }
        try {
            // main thread wait sub thread finish opt
            cdl.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        pool.shutdown();
        System.out.println("the count expect 10000, and the safe count result is " + count);


        ExecutorService pool2 = Executors.newFixedThreadPool(num);
        for(int i = 0; i < num; i++){
            pool2.submit(unSafeTask);
        }
        try {
            cdl2.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        pool2.shutdown();
        System.out.println("the count expect 10000, and the unsafe count2 result is " + count2);

    }

    /**
     * 两个线程 同时访问 同一个对象的同步方法
     * 场景1：
     * 两个线程对 对同一个全局变量各自执行 100次count++
     */
    public void screen1(){
        // 代码快 方式
        lockTheCodeBlock();
        // 方法锁的方式类似，关键字放在方法上，功能相同，此处略去
    }

    /**
     * two thread map two instance(same class)
     *
     * result:
     * pool-1-thread-1 begin.
     * pool-1-thread-2 begin.
     * pool-1-thread-2 end.
     * pool-1-thread-1 end.
     *
     * view:
     * we can see thread1 and thread2 both begin
     * and then
     * thread1 and thread2 both end
     *
     * although our code synchronized but diff instance task
     * in diff thread will lead
     *
     */
    public void diffThreadMapDiffInstance(){
        MyBlockTaskAtMethod task = new MyBlockTaskAtMethod();
        MyBlockTaskAtMethod task2 = new MyBlockTaskAtMethod();
        int num = 2;
        ExecutorService pool = Executors.newFixedThreadPool(num);
        // mark we add cdl3 in task
        pool.submit(task);
        pool.submit(task2);
        // so here can use await to wait task fnish
        try {
            cdl3.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        pool.shutdown();
        System.out.println("count is " + count);
        System.out.println("finish.");
    }

    /**
     * 两个线程 同时访问 两个对象的方法
     *
     * 结论：
     * 两个线程同时访问两个对象的同步方法，线程是不安全的
     * 原因：两个线程是并行执行的，多个线程不再竞争同一把锁，所以线程不安全
     */
    public void screen2(){
        diffThreadMapDiffInstance();
    }

    /**
     * 两个线程同时 访问 一个或者两个 对象的静态同步方法
     *
     * 结论：
     * 只需要在 场景2的基础上 把同步方法用 static修饰
     * 这样就形成 类锁，多个实例（instance1, instance2）都共同竞争同一把锁
     */
    public void screen3(){

    }

    /**
     * 两个线程同时访问 对象的同步方法和非同步方法
     *
     * 结果：
     * pool-1-thread-1 同步方法 begin.
     * pool-1-thread-2 普通方法 begin
     * pool-1-thread-1 同步方法 end.
     * pool-1-thread-2 普通方法 end
     *
     * 结论：
     * 非同步方法 不会收到 其他由synchronized修饰的同步方法影响
     * 所以线程是不安全的
     */
    public void screen4(){
        MyBlockTaskRunUnSafeFunc task = new MyBlockTaskRunUnSafeFunc();
        int num = 2;
        ExecutorService pool = Executors.newFixedThreadPool(num);
        // mark we add cdl3 in task
        pool.submit(task);
        pool.submit(task);
        // so here can use await to wait task fnish
        try {
            cdl3.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        pool.shutdown();
        System.out.println("finish.");
    }

    /**
     * 同步方法中调用 非同步方法
     *
     * 结论：
     * 如果过该 非同步方法没有被其他的线程调用，那么是安全的
     * 但是一旦 该非同步方法被其他的线程调用，那么线程是不安全的
     *
     * 解决方法：
     * 在非同步方法中也加上synchronized关键字
     */
    public void screen5(){

    }

    /**
     * 两个线程 同时访问 同一对对象的不同 同步方法
     *
     * 结果：
     * pool-1-thread-2 同步方法 begin.
     * pool-1-thread-2 同步方法 end.
     * pool-1-thread-1 同步方法 begin.
     * pool-1-thread-1 同步方法 end.
     *
     * 结论：
     * 我们可以看到 完成了各自线程 才会接到下一个 线程去执行
     * 线程是安全的
     *
     * 原因：
     * 锁对象默认是this对象，为默认锁对象
     * 体现了 synchronized关键字的重入性
     */
    public void screen6(){
        MyBlockFun1AndFun2 task = new MyBlockFun1AndFun2();
        int num = 2;
        ExecutorService pool = Executors.newFixedThreadPool(num);
        // mark we add cdl3 in task
        pool.submit(task);
        pool.submit(task);
        // so here can use await to wait task fnish
        try {
            cdl3.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        pool.shutdown();
        System.out.println("finish.");
    }

    /**
     * 两个线程同时 访问同一个对象的 静态同步方法 和 非静态同步方法
     * 线程1访问 静态同步方法
     * 线程2访问 非静态同步方法
     *
     * 结论：
     * 线程不安全
     *
     * 原因：
     * 由于访问 静态同步方法 锁住的是 .class 对象
     * 非静态同步方法 锁住的是 this 类锁的对象
     *
     */
    public void screen7(){

    }

    /**
     * 两个线程同时 访问同一个对象的 同步方法
     * 其中一个同步方法抛出异常，是否会死锁？
     *
     * 结果：
     * pool-1-thread-1 同步方法 begin.
     * pool-1-thread-1 抛出异常 end.
     * pool-1-thread-2 同步方法 begin.
     * pool-1-thread-2 同步方法 end.
     *
     * 结论：
     * 线程安全，不会死锁
     *
     * 原因：
     * 其中一个线程 抛出异常，就会释放锁，不会造成死锁
     *
     */
    public void screen8(){
        MyBlockFun1ThrowsAndFun2 task = new MyBlockFun1ThrowsAndFun2();
        int num = 2;
        ExecutorService pool = Executors.newFixedThreadPool(num);
        // mark we add cdl3 in task
        pool.submit(task);
        pool.submit(task);
        // so here can use await to wait task fnish
        try {
            cdl3.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        pool.shutdown();
        System.out.println("finish.");

    }


    public static void main(String[] args) {
        SynchronizedMain main = new SynchronizedMain();
        main.screen1();
//        main.screen2();
//        main.screen4();
//        main.screen6();
//        main.screen8();
    }

}
